/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLErrorBar.h>
#include <pgl/core/PGLDataVectors.h>
#include <pgl/core/DataRegistry.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

PGL_IMPLEMENT_DYNCREATE(CPGLErrorBar, CPGLLine2D);
PGL_IMPLEMENT_SCRIPTING(ErrorBar);

/*
void CPGLErrorBar::Help(CString& str) const
{
	str.Empty(); 
	PropertyMap::const_iterator it; 
		
	str+=_T("PGLErrorbar :\r\n-----------------\r\n");
	for (it= m_mProps.begin();it!= m_mProps.end(); ++it)
	{
		str+=it->first;
		str+=_T("\t");
		str+=it->second.second;
		str+=_T("\r\n");
	};
};

CPGLErrorBar::EErrorBarProp CPGLErrorBar::StringToErrorBarProp(LPCTSTR szProp)
{	
	static const CString sClassName(_T("ErrorBar."));
	return (EErrorBarProp)m_mProps[sClassName+szProp].first;
};
*/

//! Save settings
void CPGLErrorBar::SerializeXML(CPGLArchive& ar)
{
	CString strPos;	strPos.Format(_T("%u"),(DWORD)this);
	static const TCHAR szTag[] = _T("CPGLErrorBar");

	CString datas, str;
	std::_tstring strX,strY;

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		ASSERT(m_pX);
		ASSERT(m_pY);

		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();
		
			// base class
			CPGLLine2D::SerializeXML(ar);

			xml.AddChildElemEx(_T("Width"), m_dErrorBarWidth);
			xml.AddChildElemEx(_T("Vertical"),m_bVertical);
			xml.AddChildElemEx(_T("Absolute"),m_bAbsolute);
			xml.AddChildElemEx(_T("BaseLine"),m_bShowBaseLine);


			xml.AddChildElemEx(_T("Data"));
			xml.IntoElem();
				if (m_pErrorUp)
				{
					xml.AddChildElemEx(_T("Up"));
					xml.IntoElem();
						m_pErrorUp->SerializeXML(ar);
					xml.OutOfElem();
				}
				if (m_pErrorDown)
				{
					xml.AddChildElemEx(_T("Down"));
					xml.IntoElem();
						m_pErrorDown->SerializeXML(ar);
					xml.OutOfElem();
				}
			xml.OutOfElem();

		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLLine2D::SerializeXML(ar);

			xml.FindGetChildData(_T("Width"), m_dErrorBarWidth);
			xml.FindGetChildData(_T("Vertical"),m_bVertical);
			xml.FindGetChildData(_T("Absolute"),m_bAbsolute);
			xml.FindGetChildData(_T("BaseLine"),m_bShowBaseLine);

			//////////////////////////////////////////////////////////////////////////:
			/// Datas....
			if (xml.FindChildElem(_T("Data")))
			{
				xml.IntoElem();

				PGL_SERIALIZE_LOAD_DATA( strPos , Up , m_pErrorUp );
				PGL_SERIALIZE_LOAD_DATA( strPos , Down , m_pErrorDown );

				xml.OutOfElem();
			}

		xml.OutOfElem();

		PostUpdateExtent();
	}
}

#ifdef _DEBUG
void CPGLErrorBar::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLLine2D::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("CPGLErrorBar ID ") << GetID() << "\n";
}
void CPGLErrorBar::AssertValid() const
{
    // call inherited AssertValid first
    CPGLLine2D::AssertValid();

    // check members...
	ASSERT(m_pErrorUp);
	ASSERT(m_pErrorDown);
} 
#endif


CPGLErrorBar::CPGLErrorBar(const CPGLColor& _FrontColor, const CPGLColor& _FillColor)
: CPGLLine2D(_FrontColor, _FillColor),
m_bAbsolute(true), m_bVertical(true), m_bShowBaseLine(false),m_dErrorBarWidth(10)
{
	m_pErrorUp.reset();
	m_pErrorDown.reset();
	PreparePropertyMap();
}

CPGLErrorBar& CPGLErrorBar::operator = (const CPGLErrorBar& l)
{
	// prevent self copy
	if (&l != this)
	{
		// invoke CPGLErrorBar copy assignement operator
		this->CPGLLine2D::operator=(l);

		m_bAbsolute=l.m_bAbsolute;
		m_bVertical=l.m_bVertical;
		m_bShowBaseLine= l.m_bShowBaseLine;
		m_dErrorBarWidth = l.m_dErrorBarWidth;

		ClearErrorData();
		if (l.m_pErrorUp)
			m_pErrorUp=CPGLDataPtr(l.m_pErrorUp->Clone());
		if (l.m_pErrorDown)
			m_pErrorUp=CPGLDataPtr(l.m_pErrorDown->Clone());
	}
	return *this;
}

CPGLErrorBar::~CPGLErrorBar()
{
	ClearErrorData();
}

void CPGLErrorBar::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	CPGLLine2D::UpdateExtent(gfx);

	size_t iLeft, iRight, iBottom, iTop,i;
	size_t nPoints=GetNPoints();
	if (nPoints == 0)
	{
		SetExtent( CPGLRectD::GetNormBox() );
		return;
	}
	// updating...
	CPGLRectD rExtent=CPGLRectD::GetNormBox();

	if (m_bVertical)
	{
		// left right extent...
		if (m_pX)
		{
			m_pX->GetMinMaxIndex(iLeft, iRight);
			rExtent.left = (*m_pX)[iLeft];
			rExtent.right = (*m_pX)[iRight];
		}
		// absolute data
		if (m_bAbsolute)
		{
			iBottom=m_pErrorDown->GetMinIndex();
			iTop=m_pErrorUp->GetMaxIndex();

			rExtent.bottom=(*m_pErrorDown)[iBottom];
			rExtent.top=(*m_pErrorUp)[iTop];
		}
		// relative data
		else
		{
			double bot,top;
			rExtent.bottom=(*m_pY)[0]-(*m_pErrorDown)[0];
			rExtent.top=(*m_pY)[0]+(*m_pErrorUp)[0];
			for (i=1;i<nPoints;++i)
			{
				bot=(*m_pY)[i]-(*m_pErrorDown)[i];
				top=(*m_pY)[i]+(*m_pErrorUp)[i];
				rExtent.bottom=__min(bot, rExtent.bottom);
				rExtent.top=__max(top, rExtent.top);				
			}
		}
	}
	// horizontal
	else
	{
		// left right extent...
		if (m_pY)
		{
			m_pY->GetMinMaxIndex(iBottom, iTop);
			rExtent.bottom = (*m_pY)[iBottom];
			rExtent.top = (*m_pY)[iTop];
		}
		// absolute data
		if (m_bAbsolute)
		{
			iLeft=m_pErrorDown->GetMinIndex();
			iRight=m_pErrorUp->GetMaxIndex();

			rExtent.left=(*m_pErrorDown)[iLeft];
			rExtent.right=(*m_pErrorUp)[iRight];
		}
		// relative data
		else
		{
			double left,right;
			rExtent.left=(*m_pX)[0]-(*m_pErrorDown)[0];
			rExtent.right=(*m_pX)[0]+(*m_pErrorUp)[0];
			for (i=1;i<nPoints;++i)
			{
				left=(*m_pX)[i]-(*m_pErrorDown)[i];
				right=(*m_pX)[i]+(*m_pErrorUp)[i];
				rExtent.left=__min(left, rExtent.left);
				rExtent.right=__max(right, rExtent.right);				
			}
		}
	}

	// setting extent
	SetExtent(rExtent);
}

void CPGLErrorBar::SetErrorDatas(const std::vector<double>& vErrorUp, const std::vector<double>& vErrorDown, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		CPGLVectorDPtr veu(new CPGLVectorD(vErrorUp)); 
		CPGLVectorDPtr ved(new CPGLVectorD(vErrorDown));
		SetErrorDatas(veu,ved);
	}
	else
	{
		CPGLVectorRefDPtr veu(new CPGLVectorRefD(vErrorUp));
		CPGLVectorRefDPtr ved (new CPGLVectorRefD(vErrorDown));
		SetErrorDatas(veu,ved);
	}
}

void CPGLErrorBar::SetErrorDatas(const std::vector<float>& vErrorUp, const std::vector<float>& vErrorDown, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		CPGLVectorFPtr veu(new CPGLVectorF(vErrorUp)); 
		CPGLVectorFPtr ved(new CPGLVectorF(vErrorDown));
		SetErrorDatas(veu,ved);
	}
	else
	{
		CPGLVectorRefFPtr veu(new CPGLVectorRefF(vErrorUp));
		CPGLVectorRefFPtr ved (new CPGLVectorRefF(vErrorDown));
		SetErrorDatas(veu,ved);
	}
}

void CPGLErrorBar::SetErrorDatas(const std::vector<int>& vErrorUp, const std::vector<int>& vErrorDown, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		CPGLVectorIPtr veu(new CPGLVectorI(vErrorUp)); 
		CPGLVectorIPtr ved(new CPGLVectorI(vErrorDown));
		SetErrorDatas(veu,ved);
	}
	else
	{
		CPGLVectorRefIPtr veu(new CPGLVectorRefI(vErrorUp));
		CPGLVectorRefIPtr ved (new CPGLVectorRefI(vErrorDown));
		SetErrorDatas(veu,ved);
	}
}

void CPGLErrorBar::SetErrorDatas(const std::vector<BYTE>& vErrorUp, const std::vector<BYTE>& vErrorDown, bool bMakeCopy)
{
	if (bMakeCopy)
	{
		CPGLVectorBPtr veu(new CPGLVectorB(vErrorUp)); 
		CPGLVectorBPtr ved(new CPGLVectorB(vErrorDown));
		SetErrorDatas(veu,ved);
	}
	else
	{
		CPGLVectorRefBPtr veu(new CPGLVectorRefB(vErrorUp));
		CPGLVectorRefBPtr ved (new CPGLVectorRefB(vErrorDown));
		SetErrorDatas(veu,ved);
	}
}

void CPGLErrorBar::SetErrorDatas(CPGLDataPtr pErrorUp, CPGLDataPtr pErrorDown)
{
	ClearErrorData();

	m_pErrorUp = pErrorUp;
	m_pErrorDown = pErrorDown;

	PostUpdateExtent();
}

void CPGLErrorBar::ClearErrorData()
{
	m_pErrorUp.reset();
	m_pErrorDown.reset();
}

void CPGLErrorBar::PlotLineStripGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!IsVisible())
		return;

	// plotting line
	if (m_bShowBaseLine)
		CPGLLine2D::PlotLineStripGfx(gfx);

	if (!m_pErrorUp || !m_pErrorDown || __min( m_pErrorUp->size(), m_pErrorDown->size() ) == 0 )
		return;

	// plotting error bars...
	gfx.DrawErrorBars( *GetX().get(), *GetY().get(), *m_pErrorUp.get(), *m_pErrorDown.get(), m_dErrorBarWidth, m_bAbsolute, m_bVertical);
}


void CPGLErrorBar::PreparePropertyMap()
{
	using namespace std;
	static bool bLoaded(false);
	
	if (bLoaded)
		return;
	bLoaded=true;

	CPGLObject::PreparePropertyMap();

	m_mProps[_T("ErrorBar.vertical")]=PropertyPair(ErrorBarPropVertical, _T("vertical: Vertical (true) or horizontal (false)"));
	m_mProps[_T("ErrorBar.absolute")]=PropertyPair(ErrorBarPropAbsolute, _T("absolute: Absolute (true) or relative (false)"));
	m_mProps[_T("ErrorBar.baseline")]=PropertyPair(ErrorBarPropBaseLine, _T("baseline: Show Base line (true), Hide (false)"));
	m_mProps[_T("ErrorBar.barwidth")]=PropertyPair(ErrorBarPropBarWidth, _T("barwidth: Error bar width,\r\n\twidth (in pixels)"));
}

bool CPGLErrorBar::SetProperty( LPCTSTR szProp, LPCTSTR szValue)
{
	if (CPGLLine2D::SetProperty(szProp, szValue))
		return true;

	CString sv(szValue);	
	sv.MakeLower();

	double d=0;
	switch( StringToErrorBarProp( szProp ))
	{
	case ErrorBarPropVertical:
		SetVertical( !(sv==_T("false") || sv==_T("off")) );
		return true;
	case ErrorBarPropAbsolute:
		SetAbsolute( !(sv==_T("false") || sv==_T("off")) );
		return true;
	case ErrorBarPropBaseLine:
		SetShowLine( !(sv==_T("false") || sv==_T("off")) );
		return true;
	case ErrorBarPropBarWidth:
		if (_stscanf( sv, _T("%f"), &d) == 1)
			SetBarWidth(d);
		return true;
	default:
		return false;
	};
}

bool CPGLErrorBar::Get( LPCTSTR szProp, CString& str)
{
	if(CPGLLine2D::Get(szProp, str))
		return true;

	switch( StringToErrorBarProp( szProp ))
	{
	case ErrorBarPropVertical:
		str= (IsVertical()) ? _T("true") : _T("false");
		return true;
	case ErrorBarPropAbsolute:
		str= (IsAbsolute()) ? _T("true") : _T("false");
		return true;
	case ErrorBarPropBaseLine:
		str= (IsShowLine()) ? _T("true") : _T("false");
		return true;
	case ErrorBarPropBarWidth:
		str.Format(_T("%g"), GetBarWidth());
		return true;
	default:
		return false;
	};
}
